import 'dart:async';

import 'package:flutter/widgets.dart';
import '../bloc.dart';
import 'package:provider/single_child_widget.dart';

mixin BlocListenerSingleChildWidget on SingleChildWidget {}

typedef BlocWidgetListener<S> = void Function(BuildContext context, S state);

typedef BlocListenerCondition<S> = bool Function(S previous, S current);

/// 该类目前最大的区别在于listener函数返回为空，和提供了child参数；
/// 在widget之间加多一层， 监听bloc状态处理逻辑
class BlocListener<B extends Bloc<dynamic, S>, S> extends BlocListenerBase<B, S>
    with BlocListenerSingleChildWidget {
  final Widget child;

  const BlocListener({
    Key key,
    @required BlocWidgetListener<S> listener,
    B bloc,
    BlocListenerCondition<S> condition,
    this.child,
  })  : assert(listener != null),
        super(
          key: key,
          child: child,
          listener: listener,
          bloc: bloc,
          condition: condition,
        );
}

/// BlocListenerBase 是继承自statefulWidget，有其生命周期，内部维护了订阅和取消订阅
abstract class BlocListenerBase<B extends Bloc<dynamic, S>, S>
    extends SingleChildStatefulWidget {
  final Widget child;

  final B bloc;

  final BlocWidgetListener<S> listener;

  final BlocListenerCondition<S> condition;

  const BlocListenerBase({
    Key key,
    this.listener,
    this.bloc,
    this.child,
    this.condition,
  }) : super(key: key, child: child);

  @override
  SingleChildState<BlocListenerBase<B, S>> createState() =>
      _BlocListenerBaseState<B, S>();
}

class _BlocListenerBaseState<B extends Bloc<dynamic, S>, S>
    extends SingleChildState<BlocListenerBase<B, S>> {
  StreamSubscription<S> _subscription;
  S _previousState;
  B _bloc;

  @override
  void initState() {
    super.initState();
    _bloc = widget.bloc ?? BlocProvider.of<B>(context);
    _previousState = _bloc?.state;
    _subscribe();
  }

  @override
  void didUpdateWidget(BlocListenerBase<B, S> oldWidget) {
    super.didUpdateWidget(oldWidget);
    final oldState = oldWidget.bloc ?? BlocProvider.of<B>(context);
    final currentState = widget.bloc ?? oldState;
    if (oldState != currentState) {
      if (_subscription != null) {
        _unsubscribe();
        _bloc = widget.bloc ?? BlocProvider.of<B>(context);
        _previousState = _bloc?.state;
      }
      _subscribe();
    }
  }

  @override
  Widget buildWithChild(BuildContext context, Widget child) => child;

  @override
  void dispose() {
    _unsubscribe();
    super.dispose();
  }

  void _subscribe() {
    if (_bloc != null) {
      _subscription = _bloc.skip(1).listen((state) {
        if (widget.condition?.call(_previousState, state) ?? true) {
          widget.listener(context, state);
        }
        _previousState = state;
      });
    }
  }

  void _unsubscribe() {
    if (_subscription != null) {
      _subscription.cancel();
      _subscription = null;
    }
  }
}
